import 'dart:async';
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/billing_client_wrappers.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
import 'package:zootix/models/zt_coin_goods.model.dart';
import 'package:zootix/models/zt_order.model.dart';
import 'package:zootix/store/zt_auth.store.dart';
import 'package:zootix/util/zt_util.dart';
import 'package:zootix/apis/zt-api.dart' as Api;

class ZtPurchaseService {
  static ZtPurchaseService get to => Get.find();
  late InAppPurchase _inAppPurchase;
  late StreamSubscription<List<PurchaseDetails>> _subscription;
  String? purchaseGoodsCode;
  String? purchaseOrderNo;
  bool isPurchasing = false;
  Future<ZtPurchaseService> init() async {
    _inAppPurchase = InAppPurchase.instance;
    final Stream<List<PurchaseDetails>> purchaseUpdated =
        _inAppPurchase.purchaseStream;
    _subscription =
        purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) {
      _listenToPurchaseUpdated(purchaseDetailsList);
    }, onDone: () {
      _subscription.cancel();
    }, onError: (Object error) {
      // handle error here.
      ZtSnackBarUtil().error('$error');
    });

    return this;
  }

  /// in app purchase
  Future<void> applePay(String goodsCode, String goodsId) async {
    if (isPurchasing) {
      ZtSnackBarUtil()
          .warning('There is an ongoing payment order, please wait');
      return;
    }
    isPurchasing = true;

    /// _inAppPurchase is valid
    final bool isAvailable = await _inAppPurchase.isAvailable();
    if (!isAvailable) {
      isPurchasing = false;
      return;
    }

    if (Platform.isIOS) {
      final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition =
          _inAppPurchase
              .getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
      await iosPlatformAddition.setDelegate(ExamplePaymentQueueDelegate());
    }

    /// is product resigisted in apple
    final ProductDetailsResponse productDetailResponse =
        await _inAppPurchase.queryProductDetails({goodsCode});
    if (productDetailResponse.error != null) {
      ZtLoadingUtil.dismiss();
      isPurchasing = false;
      Get.defaultDialog(
          title: 'Get product info failed.',
          content: const Text('Can\'t find product id from apple'));
      return;
    }

    if (productDetailResponse.productDetails.isEmpty) {
      ZtLoadingUtil.dismiss();
      isPurchasing = false;
      Get.defaultDialog(
          title: 'Get product info failed.',
          content: const Text('Can\'t find product id from apple'));

      return;
    }
    ProductDetails productDetails = productDetailResponse.productDetails[0];
    late PurchaseParam purchaseParam;

    ZtOrderInfo? orderInfo = await _createOrder(goodsCode, goodsId);

    if (orderInfo == null) {
      ZtSnackBarUtil().error('Create order failed. Please try again.');
      isPurchasing = false;
      return;
    }
    purchaseGoodsCode = goodsCode;
    purchaseOrderNo = orderInfo.orderNo;
    purchaseParam = PurchaseParam(
        productDetails: productDetails, applicationUserName: orderInfo.orderNo);
    await ZtLoadingUtil.show(status: 'Requesting Apple Pay...');

    /// apple pay request
    try {
      await _inAppPurchase.buyConsumable(purchaseParam: purchaseParam);
    } catch (e) {
      ZtSnackBarUtil().error('Requesting Apple Pay failed. Please try again.');
    } finally {
      await ZtLoadingUtil.dismiss();
    }
  }

  Future<void> _listenToPurchaseUpdated(
      List<PurchaseDetails> purchaseDetailsList) async {
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      if (purchaseDetails.status == PurchaseStatus.pending) {
      } else if (purchaseDetails.status == PurchaseStatus.canceled) {
        _inAppPurchase.completePurchase(purchaseDetails);
        isPurchasing = false;
        ZtLoadingUtil.dismiss();
      } else {
        if (purchaseDetails.status == PurchaseStatus.error) {
          ZtLoadingUtil.dismiss();
          Get.defaultDialog(title: 'Purchase failed.');
          _inAppPurchase.completePurchase(purchaseDetails);
          isPurchasing = false;
        } else if (purchaseDetails.status == PurchaseStatus.purchased ||
            purchaseDetails.status == PurchaseStatus.restored) {
          _inAppPurchase.completePurchase(purchaseDetails);
          isPurchasing = false;
          _paySuccess(purchaseDetails);
        }
      }
    }
  }

  Future<bool> _validateOrder(PurchaseDetails purchaseDetails) async {
    if (purchaseOrderNo != null) {}
    final res = await Api.validateOrder(
        orderNo: purchaseOrderNo!,
        payload: purchaseDetails.verificationData.serverVerificationData,
        transactionId: purchaseDetails.purchaseID ?? '');
    if (res.data != null && res.data['success']) {
      return true;
    }
    return false;
  }

  Future<void> _paySuccess(PurchaseDetails? purchaseDetails) async {
    if (purchaseDetails != null) {
      bool isValid = await _validateOrder(purchaseDetails);
      if (isValid) {
        await ZtAuthStore.to.getUserInfo();
        Get.back();
        return;
      }
    }
  }

  Future<void> consumeCoin(int number) async {
    await ZtLoadingUtil.show(status: 'Paying...');
    try {
      final response = await Api.reviewModeConsume(num: number);
      if (response.data != null && response.data['success']) {
        ZtLoadingUtil.dismiss();
      }
    } catch (e) {
      ZtSnackBarUtil().error('Purchasing failed.: $e');
    } finally {
      ZtLoadingUtil.dismiss();
    }
  }

  Future<ZtOrderInfo?> _createOrder(String goodsCode, String goodsId) async {
    await ZtLoadingUtil.show(status: 'Creating Order...');
    try {
      final response = await Api.createOrder(goodsCode: goodsCode);
      if (response.data != null && response.data['success']) {
        ZtLoadingUtil.dismiss();
        ZtOrderInfo orderInfo = ZtOrderInfo.fromJson(response.data['data']);
        return orderInfo;
      }
    } catch (e) {
      ZtSnackBarUtil().error('Create order error: $e');
    } finally {
      ZtLoadingUtil.dismiss();
    }
    return null;
  }
}

class ExamplePaymentQueueDelegate implements SKPaymentQueueDelegateWrapper {
  @override
  bool shouldContinueTransaction(
      SKPaymentTransactionWrapper transaction, SKStorefrontWrapper storefront) {
    return true;
  }

  @override
  bool shouldShowPriceConsent() {
    return false;
  }
}
